package pers.vic.boot.security.redis;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Vic.xu
 */
@Configuration
@ConditionalOnExpression("${auth.redis.enabled:true}")
public class RedisConfig {

    /**
     * 配置定制化的缓存超时时间， 单位 小时
     * 形如：customer.cache.map.name1=2 表示name1的缓存时间配置为2小时
     */
    @Value("#{${customer.cache.map:{}}}")
    public Map<String, Integer> customerCacheNameMap;

    /**
     * 使用Lettuce 连接池的方式
     * @param lettuceConnectionFactory
     * @return
     */
    @Bean("redisTemplateCustomize")
    @ConditionalOnBean(LettuceConnectionFactory.class)
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        // 配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        // key序列化
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // value序列化
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     *
     * /** 缓存配置管理器
     */
    @Bean
    @ConditionalOnMissingBean
    public CacheManager cacheManager(LettuceConnectionFactory factory) {
        // 以锁写入的方式创建RedisCacheWriter对象
        RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);
        // 创建默认缓存配置对象
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                //默认失效时间 2天
                .entryTtl(Duration.ofDays(2))
                .serializeKeysWith(
                        RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()))
                //不缓存null
                .disableCachingNullValues();
        //定制化某些缓存的失效时间
        Map<String, RedisCacheConfiguration> expires = expires(config);

        //RedisCacheManager cacheManager = new RedisCacheManager(writer, config);
        RedisCacheManager cacheManager = RedisCacheManager.RedisCacheManagerBuilder.fromCacheWriter(writer)
                .cacheDefaults(config)
                //synchronize cache put/evict operations with ongoing Spring-managed transactions.
                .transactionAware()
                .initialCacheNames(expires.keySet())
                .withInitialCacheConfigurations(expires)
                .build();
        return cacheManager;

    }

    /**
     * redisService
     */
    @Bean
    @ConditionalOnMissingBean
    public RedisService redisService(@Qualifier("redisTemplateCustomize") RedisTemplate<String, Object> redisTemplate) {
        return new RedisService(redisTemplate);
    }

    /**
     * 定制化某些缓存的失效时间
     * @param config
     * @return
     */
    public Map<String, RedisCacheConfiguration> expires(RedisCacheConfiguration config) {
        Map<String, RedisCacheConfiguration> expires = new HashMap<String, RedisCacheConfiguration>();
        if (customerCacheNameMap != null) {
            customerCacheNameMap.forEach((key, value) -> {
                expires.put(key, config.entryTtl(Duration.ofHours(value)));
            });
        }
        return expires;

    }

}